<template>
  <!-- xmlns:v-slot="http://www.w3.org/1999/XSL/Transform" -->
  <div :class="[prefixCls]">
    <div
      :class="classes"
      @click="handleClick"
      @drop.prevent="onDrop"
      @paste="handlePaste"
      ref="container"
      @dragover.prevent="dragOver = true"
      @dragleave.prevent="dragOver = false">
      <input
        ref="input"
        type="file"
        :class="[prefixCls + '-input']"
        @change="handleChange"
        :multiple="multiple"
        :accept="accept">
      <slot v-if="!$slots.trigger">
        <div v-if="type === 'drag' || type === 'drag-1'" :class="dragContentClasses">
          <Icon type="a-Upload2"></Icon>
          &nbsp;
          <div :class="[prefixCls + '-content-drag-default-text-details']">拖拽文件到此处或者</div>
          <div :class="[prefixCls + '-content-drag-default-text-strong']">&nbsp;&nbsp; 点击上传</div>
        </div>
      </slot>

      <div class="haloe-upload-trigger" v-if="$slots.trigger">
        <slot name="trigger"></slot>
      </div>
    </div>
    <slot v-if="$slots.trigger && $slots.default" />
    <slot name="tip"></slot>
    <slot name="multipleList" :fileList="fileList">
      <upload-list
        v-if="showUploadList"
        :files="fileList"
        @on-file-remove="handleRemove"
        @on-file-preview="handlePreview">
        <template v-slot:details="{ file }">
          <slot name="fileDetails" :file="file"></slot>
        </template>
      </upload-list>
    </slot>
  </div>
</template>
<script>
import UploadList from './upload-list.vue';
import ajax from './ajax';
import { oneOf } from '../../utils/assist';
import mixinsForm from '../../mixins/form';
import Icon from '../icon/icon.vue';

const prefixCls = 'haloe-upload';
export default {
  name: 'Upload',
  mixins: [mixinsForm],
  components: { UploadList, Icon },
  props: {
    action: {
      type: String,
      required: true
    },
    headers: {
      type: Object,
      default() {
        return {};
      }
    },
    multiple: {
      type: Boolean,
      default: false
    },
    data: {
      type: Object
    },
    name: {
      type: String,
      default: 'file'
    },
    withCredentials: {
      type: Boolean,
      default: false
    },
    showUploadList: {
      type: Boolean,
      default: true
    },
    type: {
      type: String,
      validator(value) {
        return oneOf(value, ['select', 'drag', 'drag-1']);
      },
      default: 'select'
    },
    format: {
      type: Array,
      default() {
        return [];
      }
    },
    accept: {
      type: String,
      default: '',
    },
    maxSize: {
      type: Number
    },
    beforeUpload: Function,
    onCloseSelectFileWindow: Function,
    onProgress: {
      type: Function,
      default() {
        return {};
      }
    },
    onSuccess: {
      type: Function,
      default() {
        return {};
      }
    },
    onError: {
      type: Function,
      default() {
        return {};
      }
    },
    onRemove: {
      type: Function,
      default() {
        return {};
      }
    },
    onPreview: {
      type: Function,
      default() {
        return {};
      }
    },
    onExceededSize: {
      type: Function,
      default() {
        return {};
      }
    },
    onFormatError: {
      type: Function,
      default() {
        return {};
      }
    },
    defaultFileList: {
      type: Array,
      default() {
        return [];
      }
    },
    paste: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    webkitdirectory: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      prefixCls: prefixCls,
      dragOver: false,
      fileList: [],
      tempIndex: 1,
      manualFiles: [],
      trigger: false,
    };
  },
  computed: {
    mainClasses() {
      return [`${prefixCls}`, {
        [`${prefixCls}-drag-main`]: this.type === 'drag',
        [`${prefixCls}-drag-1-main`]: this.type === 'drag-1'
      }]
    },
    dragContentClasses() {
      return {
        [`${prefixCls}-content-drag-default`]: this.type === 'drag',
        [`${prefixCls}-content-drag-1-default`]: this.type === 'drag-1'
      }
    },
    classes() {
      return [
        `${prefixCls}`,
        `${prefixCls}-hover-border-color`,
        {
          [`${prefixCls}-select`]: this.type === 'select',
          [`${prefixCls}-drag`]: this.type === 'drag' || this.type === 'drag-1',
          [`${prefixCls}-dragOver`]: (this.type === 'drag' || this.type === 'drag-1') && this.dragOver,
          [`${prefixCls}-disbaled`]: this.disabled,
        }
      ]
    }
  },
  methods: {
    handleClick() {
      if (this.itemDisabled || this.disabled) return;
      this.$refs.input.click();
      // 模拟取消事件
      window.addEventListener(
        'focus',
        (e) => {
          this.$refs.container.classList.remove(`${prefixCls}-hover-border-color`);
          window.addEventListener(
            'mousemove',
            (e) => {
              this.$refs.container.classList.add(`${prefixCls}-hover-border-color`);
            },
            { once: true }
          )

          if (this.onCloseSelectFileWindow) {
            this.onCloseSelectFileWindow()
          }
        },
        { once: true }
      )
    },
    handleManual(files) {
      let postFiles = Array.prototype.slice.call(files);
      if (!this.multiple) postFiles = postFiles.slice(0, 1);
      if (postFiles.length === 0) return;
      postFiles.forEach(file => {
        this.handleStart(file);
      });
      this.manualFiles = this.manualFiles.concat(postFiles);
    },
    handleUploadFiles() {
      if (this.manualFiles.length === 0) return;
      this.manualFiles.forEach(file => {
        this.upload(file)
      })

    },
    handleCancelAll() {
      this.fileList.forEach(item => {
        if (item.status === 'uploading') {
          item.xhr.abort();
          item.status = 'fail'
        }
      })
    },
    handleCancelOne(uid) {
      const _file = this.fileList.find(item => uid === item.uid)
      if (_file.status === 'uploading') {
        _file.xhr.abort();
        _file.status = 'fail'
      }
    },
    handleDeleteAll() {
      this.fileList = [];
      this.fileList.length = 0;
      this.manualFiles = [];
      this.manualFiles.length = 0;
    },
    handleDeleteOne(uid) {
      const findId = (item) => item.uid === uid
      const index = this.fileList.findIndex(findId);
      this.fileList.splice(index, 1)
      const _index = this.manualFiles.findIndex(findId)
      this.manualFiles.splice(_index, 1)
    },
    handleReUpload(file) {
      if (this.manualFiles.length === 0) return;
      const findId = (item) => item.uid === file.uid

      const index = this.fileList.findIndex(findId);
      const _file = {
        status: 'uploading',
        name: file.name,
        size: file.size,
        percentage: 0,
        uid: file.uid,
        showProgress: true,
        xhr: null,
      };
      this.fileList.splice(index, 1, _file)
      this.manualFiles.forEach(item => {
        if (item.uid === file.uid) {
          this.upload(item)
        }
      })
    },
    handleChange(e) {
      if (this.onCloseSelectFileWindow) {
        this.onCloseSelectFileWindow()
      }
      const files = e.target.files;
      if (!files) {
        return;
      }
      if (this.$slots.trigger) this.trigger = true;
      this.trigger ? this.handleManual(files) : this.uploadFiles(files);
      if (this.$refs.input) this.$refs.input.value = null;
    },
    onDrop(e) {
      this.dragOver = false;
      if (this.itemDisabled) return;
      this.uploadFiles(e.dataTransfer.files);
    },
    handlePaste(e) {
      if (this.itemDisabled) return;
      if (this.paste) {
        this.uploadFiles(e.clipboardData.files);
      }
    },
    uploadFiles(files) {
      let postFiles = Array.prototype.slice.call(files);
      if (!this.multiple) postFiles = postFiles.slice(0, 1);
      if (postFiles.length === 0) return;

      postFiles.forEach(file => {
        this.upload(file);
      });
      if (this.$slots.multipleList) this.manualFiles.push(postFiles);
    },
    upload(file) {
      if (!this.beforeUpload) {
        return this.post(file);
      }
      const before = this.beforeUpload(file);
      if (before && before.then) {
        before.then(processedFile => {
          if (Object.prototype.toString.call(processedFile) === '[object File]') {
            this.post(processedFile);
          } else {
            this.post(file);
          }
        }, () => { });
      } else if (before !== false) {
        this.post(file);
      }
    },
    post(file) {
      if (this.format.length) {
        const _file_format = file.name.split('.').pop().toLocaleLowerCase();
        const checked = this.format.some(item => item.toLocaleLowerCase() === _file_format);
        if (!checked) {
          this.onFormatError(file, this.fileList);
          return false;
        }
      }
      // check maxSize
      if (this.maxSize) {
        if (file.size > this.maxSize * 1024) {
          this.onExceededSize(file, this.fileList);
          return false;
        }
      }

      this.handleStart(file);
      const _file = this.getFile(file);
      _file.status = 'uploading'
      _file.percentage = 0
      let formData = new FormData();
      formData.append(this.name, file);
      _file.xhr = ajax({
        headers: this.headers,
        withCredentials: this.withCredentials,
        file: file,
        data: this.data,
        filename: this.name,
        action: this.action,
        onProgress: e => {
          this.handleProgress(e, file);
        },
        onSuccess: res => {
          this.handleSuccess(res, file);
        },
        onError: (err, response) => {
          this.handleError(err, response, file);
        }
      });
    },
    handleStart(file) {
      //避免重复加入fileList
      if (!file.uid) {
        file.uid = Date.now() + this.tempIndex++;
        const _file = {
          status: 'waiting',
          name: file.name,
          size: file.size,
          percentage: 0,
          uid: file.uid,
          showProgress: true,
          xhr: null,
        };
        this.fileList.push(_file);
        return _file;
      }
      return file
    },
    getFile(file) {
      return this.fileList.find(item => file.uid === item.uid)
    },
    handleProgress(e, file) {
      const _file = this.getFile(file);
      _file.percentage = parseFloat(e.percent.toFixed(2)) || 0;
      this.onProgress(e, _file, this.fileList);
    },
    handleSuccess(res, file) {
      const _file = this.getFile(file);
      if (_file) {
        _file.status = 'finished';
        _file.response = res;

        this.onSuccess(res, _file, this.fileList);
        this.handleFormItemChange('change', _file);
        setTimeout(() => {
          _file.showProgress = false;
        }, 1000);
      }
    },
    handleError(err, response, file) {
      const _file = this.getFile(file);
      _file.response = response
      this.onError(err, file, this.fileList)
      _file.status = 'fail';
    },

    handleRemove(file) {
      if (file.status === 'uploading' && file?.xhr) {
        file?.xhr.abort()
      }
      const fileList = this.fileList;
      fileList.splice(fileList.indexOf(file), 1);
      this.onRemove(file, fileList);
    },
    handlePreview(file) {
      if (file.status === 'finished') {
        this.onPreview(file);
      }
    },
  },
  unmounted() {
    this.handleCancelAll();
  },
  watch: {
    defaultFileList: {
      immediate: true,
      handler(fileList) {
        this.fileList = fileList.map(item => {
          item.status = 'finished';
          item.percentage = 100;
          item.uid = Date.now() + this.tempIndex++;
          return item;
        });
      }
    }
  },
};
</script>
